package com.greenyoung.pwiki.elasticsearch.search.mapper;

import com.greenyoung.pwiki.common.utils.DateUtils;
import com.greenyoung.pwiki.elasticsearch.search.entity.Markdown;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.SearchHit;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 高亮显示结果不属于markdown实体的一部分(返回的highlight字段不在_source中)，因此repository无法存储
 * 所以要手动创建一个从搜索返回结果到实体的映射
 * <p>
 * 接收到的字段都将在这里映射到markdown，这是完全独立的、较为底层的转化
 * 但是在创建query的时候仍需排除不需要的字段，否则会浪费时间和空间。因为它实际已经获取到数据，只是没有将对应的字段与实体建立映射而已
 * see https://stackoverflow.com/questions/37049764/how-to-provide-highlighting-with-spring-data-elasticsearch
 */
public class MarkdownSearchResultMapper implements SearchResultMapper {
    @Override
    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
        List<Markdown> chunk = new ArrayList<Markdown>();
        for (SearchHit searchHit : response.getHits()) {
            if (response.getHits().getHits().length <= 0) {
                return null;
            }

            Markdown markdown = new Markdown();
            injectData(markdown, searchHit);

            chunk.add(markdown);
        }
        if (chunk.size() > 0) {
            return new AggregatedPageImpl<T>((List<T>) chunk);
        }
        return null;
    }

    /**
     * @param searchHit el返回搜索结果的一部分
     * @param type      要映射的实体类型，这里是markdown
     * @param <T>       这里是markdown
     * @return 映射后的markdown
     */
    @Override
    public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
        if (!searchHit.hasSource()) {
            return null;
        }

        Markdown markdown = new Markdown();
        injectData(markdown, searchHit);

        return type.cast(markdown);
    }

    /**
     * 把searchHit数据注入markdown实体
     *
     * @param markdown
     * @param searchHit
     */
    private void injectData(Markdown markdown, SearchHit searchHit) {
        markdown.setId(Long.valueOf(searchHit.getId()));

        Map<String, Object> sourceMap = searchHit.getSourceAsMap();
        markdown.setName((String) sourceMap.get("name"));
        String createTimeStr = (String) sourceMap.get("create_time");
        markdown.setCreateTime(DateUtils.stringToDate(createTimeStr, DateUtils.DATE_TIME_PATTERN));

        Text[] highLightContentTexts = searchHit.getHighlightFields().get("attachment.content").fragments();
        List<String> searchHighlightContnet = new ArrayList<>();
        for (Text text : highLightContentTexts) {
            searchHighlightContnet.add(text.string());
        }
        markdown.setSearchHighlightContent(searchHighlightContnet);
    }
}
